1
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <errno.h>
6#include <stdint.h>
7#include <stdbool.h>
8
9#include <unistd.h>
10#include <fcntl.h>
11#include <sys/ioctl.h>
12#include <sys/mman.h>
13#include <linux/videodev2.h> // libv4l-dev
14
15
16#define VIDEO_DEV "/dev/video0"
17
18int main()
19{
20 // 打开设备
21 int videoFd = open(VIDEO_DEV, O_RDWR);
22 if(videoFd < 0)
23 {
24 fprintf(stderr, "open %s failed: %s\n", VIDEO_DEV, strerror(errno));
25 return EXIT_FAILURE;
26 }
27
28 // 读取设备属性
29 struct v4l2_capability videoCap;
30 if(ioctl(videoFd, VIDIOC_QUERYCAP, &videoCap) < 0)
31 {
32 close(videoFd);
33 fprintf(stderr, "ioctl VIDIOC_QUERYCAP failed: %s\n", strerror(errno));
34 return EXIT_FAILURE;
35 }
36 printf("%s\n", videoCap.card);
37
38 // 判断是否是摄像头
39 if(videoCap.capabilities & V4L2_CAP_VIDEO_CAPTURE != V4L2_CAP_VIDEO_CAPTURE)
40 {
41 fprintf(stderr, "%s doesn't support video recording\n", VIDEO_DEV);
42 close(videoFd);
43 return EXIT_FAILURE;
44 }
45
46 // 读取支持的格式
47 bool supportYUYV = false;
48 struct v4l2_fmtdesc fmtdesc;
49 fmtdesc.index=0;
50 fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
51 printf("Support format:\n");
52 while(ioctl(videoFd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
53 {
54 printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
55 fmtdesc.index++;
56 if(fmtdesc.pixelformat == V4L2_PIX_FMT_YUYV)
57 {
58 supportYUYV = true;
59 }
60 }
61
62 if(supportYUYV == false)
63 {
64 fprintf(stderr, "YUYV 4:2:2 not supported\n");
65 close(videoFd);
66 return EXIT_FAILURE;
67 }
68
69 // 设置帧格式
70 struct v4l2_format videoFormat;
71 videoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
72 videoFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
73 if(ioctl(videoFd, VIDIOC_S_FMT, &videoFormat) < 0)
74 {
75 fprintf(stderr, "ioctl VIDIOC_S_FMT failed: %s\n", strerror(errno));
76 close(videoFd);
77 return EXIT_FAILURE;
78 }
79
80 // 申请缓冲
81 struct v4l2_requestbuffers request;
82 request.count = 3; //三帧缓冲
83 request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
84 request.memory = V4L2_MEMORY_MMAP;
85 if(ioctl(videoFd, VIDIOC_REQBUFS, &request) < 0)
86 {
87 fprintf(stderr, "ioctl VIDIOC_REQBUFS failed: %s\n", strerror(errno));
88 close(videoFd);
89 return EXIT_FAILURE;
90 }
91
92 // 获取缓冲
93 struct v4l2_buffer buffer;
94 memset(&buffer, 0, sizeof(buffer));
95 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
96 buffer.memory = V4L2_MEMORY_MMAP;
97 buffer.index = 0;
98 if(ioctl(videoFd, VIDIOC_QUERYBUF, &buffer) < 0)
99 {
100 fprintf(stderr, "ioctl VIDIOC_QUERYBUF failed: %s\n", strerror(errno));
101 close(videoFd);
102 return EXIT_FAILURE;
103 }
104 void* bufPtr = mmap(NULL, buffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, videoFd, buffer.m.offset);
105 if(bufPtr == NULL)
106 {
107 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
108 close(videoFd);
109 return EXIT_FAILURE;
110 }
111
112 // 创建一个帧缓冲
113 if(ioctl(videoFd, VIDIOC_QBUF, &buffer) < 0)
114 {
115 fprintf(stderr, "ioctl VIDIOC_QBUF failed: %s\n", strerror(errno));
116 close(videoFd);
117 return EXIT_FAILURE;
118 }
119
120 // 开始采集
121 enum v4l2_buf_type bufType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
122 if(ioctl(videoFd, VIDIOC_STREAMON, &bufType) < 0)
123 {
124 fprintf(stderr, "ioctl VIDIOC_STREAMON failed: %s\n", strerror(errno));
125 close(videoFd);
126 return EXIT_FAILURE;
127 }
128
129 // 等待采集完成
130 fd_set fds;
131 FD_ZERO(&fds);
132 FD_SET(videoFd, &fds);
133 struct timeval tv;
134 tv.tv_sec = 2;
135 tv.tv_usec = 0;
136 select(1, &fds, NULL, NULL, &tv);
137
138 // 读取一帧图像并删除帧缓冲
139 if(ioctl(videoFd, VIDIOC_DQBUF, &buffer) < 0)
140 {
141 fprintf(stderr, "ioctl VIDIOC_DQBUF failed: %s\n", strerror(errno));
142 close(videoFd);
143 return EXIT_FAILURE;
144 }
145
146 // 停止采集
147 ioctl(videoFd, VIDIOC_STREAMOFF, &bufType);
148
149 // 创建一个PPM文件
150 FILE* ppmFptr = fopen("output.ppm", "wb");
151 if(ppmFptr == NULL)
152 {
153 fprintf(stderr, "%s\n", strerror(errno));
154 close(videoFd);
155 return EXIT_FAILURE;
156 }
157
158 // 写入PPM header
159 fprintf(ppmFptr, "P3\n%d %d\n255\n", videoFormat.fmt.pix.width, videoFormat.fmt.pix.height);
160
161 // 读取像素,写入PPM文件
162 for(size_t i = 0; i+3 < buffer.length; i+=4)
163 {
164 uint8_t Y1 = ((uint8_t*)bufPtr)[i];
165 uint8_t U = ((uint8_t*)bufPtr)[i+1];
166 uint8_t Y2 = ((uint8_t*)bufPtr)[i+2];
167 uint8_t V = ((uint8_t*)bufPtr)[i+3];
168
169 uint8_t B = 1.164 * (Y1 - 16) + 2.018 * (U - 128);
170 uint8_t G = 1.164 * (Y1 - 16) - 0.391 * (U - 128) - 0.813 * (V - 128);
171 uint8_t R = 1.164 * (Y1 - 16) + 1.596 * (V - 128);
172 fprintf(ppmFptr, "%u %u %u ", R, G, B);
173
174 B = 1.164 * (Y2 - 16) + 2.018 * (U - 128);
175 G = 1.164 * (Y2 - 16) - 0.391 * (U - 128) - 0.813 * (V - 128);
176 R = 1.164 * (Y2 - 16) + 1.596 * (V - 128);
177 fprintf(ppmFptr, "%u %u %u ", R, G, B);
178 }
179
180 close(videoFd);
181 fclose(ppmFptr);
182 return EXIT_SUCCESS;
183}